package cn.sciento.export;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import cn.sciento.core.util.SecurityUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import cn.sciento.core.util.Pair;
import cn.sciento.export.render.ValueRenderer;
import cn.sciento.export.util.ShardUtils;
import cn.sciento.export.vo.ExportColumn;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

public abstract class ExcelFiller {
    protected Map<String, CellStyle> stylePool = new ConcurrentHashMap();
    protected Map<Class<? extends ValueRenderer>, ValueRenderer> renderers = new ConcurrentHashMap();
    private ZoneId zoneId = ZoneId.systemDefault();
    protected Logger logger = LoggerFactory.getLogger(ExcelFiller.class);
    protected SXSSFWorkbook workbook;
    protected CellStyle titleCellStyle;
    protected Integer singleSheetMaxRow;
    private int pageIndex;

    public ExcelFiller() {
    }

    public ExcelFiller(SXSSFWorkbook workbook) {
        this.setWorkbook(workbook);
    }

    public void configure(int singleSheetMaxRow) {
        this.singleSheetMaxRow = singleSheetMaxRow;
    }

    public abstract String getFillerType();

    protected abstract void createSheetAndTitle(SXSSFWorkbook workbook, ExportColumn root);

    protected void fillDataWithShard(SXSSFWorkbook workbook, ExportColumn root, List<?> data, boolean paging) {
        if (paging) {
            String title = root.getTitle();
            root.setTitle(root.getTitle() + "-" + ++this.pageIndex);
            this.createSheetAndTitle(workbook, root);
            this.fillData(workbook, root, data);
            root.setTitle(title);
        } else {
            if (this.singleSheetMaxRow != null) {
                int sum = data.size();
                Pair<Integer, Integer> pair = ShardUtils.shard(sum, this.singleSheetMaxRow);
                int shardNum = (Integer) pair.getFirst();
                int lastNum = (Integer) pair.getSecond();
                List<List<?>> shardData = ShardUtils.prepareShardData(data, shardNum, this.singleSheetMaxRow, lastNum);
                String title = root.getTitle();
                if (shardNum == 1) {
                    root.setTitle(title);
                    this.createSheetAndTitle(workbook, root);
                    this.fillData(workbook, root, (List) shardData.get(0));
                } else {
                    for (int i = 0; i < shardNum; ++i) {
                        root.setTitle(title + "-" + (i + 1));
                        this.createSheetAndTitle(workbook, root);
                        this.fillData(workbook, root, (List) shardData.get(i));
                    }
                }

                root.setTitle(title);
            } else {
                this.createSheetAndTitle(workbook, root);
                this.fillData(workbook, root, data);
            }

        }
    }

    protected abstract void fillData(SXSSFWorkbook workbook, ExportColumn root, List<?> data);

    protected void fillCellValue(SXSSFCell cell, Object value, Object rowData, String pattern, List<Class<? extends ValueRenderer>> renderers) {
        value = this.doRender(value, rowData, renderers);
        if (value == null) {
            cell.setCellType(CellType.BLANK);
        } else {
            Class<?> type = value.getClass();
            if (value instanceof Number) {
                cell.setCellType(CellType.NUMERIC);
                cell.setCellValue(((Number) value).doubleValue());
            } else if (value instanceof Boolean) {
                cell.setCellType(CellType.BOOLEAN);
                cell.setCellValue((Boolean) value);
            } else if (value instanceof Date) {
                cell.setCellType(CellType.NUMERIC);
                cell.setCellValue((Date) value);
            } else if (value instanceof LocalDate) {
                cell.setCellType(CellType.NUMERIC);
                cell.setCellValue(Date.from(((LocalDate) value).atStartOfDay(this.zoneId).toInstant()));
            } else if (value instanceof LocalDateTime) {
                cell.setCellType(CellType.NUMERIC);
                cell.setCellValue(Date.from(((LocalDateTime) value).atZone(this.zoneId).toInstant()));
            } else {
                if (value instanceof String) {
                    cell.setCellType(CellType.STRING);
                    value = SecurityUtils.preventCsvInjection((String) value);
                    cell.setCellValue(String.valueOf(value));
                } else {
                    this.logger.warn("can not process type [{}] in excel export", type);
                    cell.setCellType(CellType.STRING);
                    value = SecurityUtils.preventCsvInjection(String.valueOf(value));
                    cell.setCellValue(String.valueOf(value));
                }
            }

            if (StringUtils.isNotBlank(pattern)) {
                cell.setCellStyle(this.pickOutCellStyle(pattern));
            }

        }
    }

    protected void setCellWidth(SXSSFSheet sheet, String type, int columnIndex) {
        if (!StringUtils.isBlank(type)) {
            String var5 = type.toUpperCase();
            byte var6 = -1;
            switch (var5.hashCode()) {
                case -1838656495:
                    if (var5.equals("STRING")) {
                        var6 = 0;
                    }
                    break;
                case -1734417839:
                    if (var5.equals("BIGDECIMAL")) {
                        var6 = 3;
                    }
                    break;
                case -1618932450:
                    if (var5.equals("INTEGER")) {
                        var6 = 5;
                    }
                    break;
                case 72655:
                    if (var5.equals("INT")) {
                        var6 = 4;
                    }
                    break;
                case 2090926:
                    if (var5.equals("DATE")) {
                        var6 = 8;
                    }
                    break;
                case 2342524:
                    if (var5.equals("LONG")) {
                        var6 = 6;
                    }
                    break;
                case 66988604:
                    if (var5.equals("FLOAT")) {
                        var6 = 1;
                    }
                    break;
                case 782694408:
                    if (var5.equals("BOOLEAN")) {
                        var6 = 7;
                    }
                    break;
                case 2022338513:
                    if (var5.equals("DOUBLE")) {
                        var6 = 2;
                    }
            }

            short columnWidth;
            switch (var6) {
                case 0:
                    columnWidth = 5632;
                    break;
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                    columnWidth = 3584;
                    break;
                case 8:
                    columnWidth = 5376;
                    break;
                default:
                    columnWidth = 5120;
            }

            sheet.setColumnWidth(columnIndex, columnWidth);
        }
    }

    protected void setTitleCellStyle() {
        Font font = this.workbook.createFont();
        font.setColor((short) 32767);
        font.setBold(true);
        this.titleCellStyle = this.workbook.createCellStyle();
        this.titleCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.index);
        this.titleCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        this.titleCellStyle.setFont(font);
        this.titleCellStyle.setAlignment(HorizontalAlignment.CENTER);
        this.titleCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
    }

    protected void setWorkbook(SXSSFWorkbook workbook) {
        this.workbook = workbook;
        this.setTitleCellStyle();
    }

    protected CellStyle pickOutCellStyle(String pattern) {
        Assert.isTrue(StringUtils.isNotBlank(pattern), "pattern should not be null");
        return (CellStyle) this.stylePool.computeIfAbsent(pattern, (key) -> {
            CellStyle s = this.workbook.createCellStyle();
            s.setDataFormat(this.workbook.createDataFormat().getFormat(pattern));
            return s;
        });
    }

    protected Object doRender(Object value, Object rowData, List<Class<? extends ValueRenderer>> rendererTypes) {
        if (CollectionUtils.isNotEmpty(rendererTypes)) {
            Iterator var4 = rendererTypes.iterator();

            while (var4.hasNext()) {
                Class<? extends ValueRenderer> rendererType = (Class) var4.next();
                ValueRenderer renderer = (ValueRenderer) this.renderers.computeIfAbsent(rendererType, (key) -> {
                    return (ValueRenderer) Optional.ofNullable(key).map((type) -> {
                        try {
                            return (ValueRenderer) type.getConstructor().newInstance();
                        } catch (Exception var3) {
                            this.logger.error("can not create renderer!", var3);
                            return null;
                        }
                    }).orElse((ValueRenderer) null);
                });
                if (renderer != null) {
                    value = renderer.render(value, rowData);
                }
            }
        }

        return value;
    }
}
